/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.p2psimulator.entity;

import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.p2psimulator.engine.Environment;
import org.p2psimulator.entity.behavior.BehaviorTypes;
import org.p2psimulator.factory.FactoryTask;
import org.p2psimulator.factory.idfactory.IdFactory;
import org.p2psimulator.factory.peerfactory.PeerFactory;

/**
 *
 * @author aamorim
 */
public class PeerBank {

    protected Map<Id, Peer> peerMap;
    private Random r;

    public PeerBank(Map<Id, Peer> peerMap) {
        this.peerMap = peerMap;
        this.r = new Random();
    }

    public Map<Id, Peer> getPeerMap() {
        return peerMap;
    }

    public int getBankSize() {
        return peerMap.size();
    }

    public final synchronized Peer getRandomPeer() {

        synchronized (peerMap) {
            Set<Id> keySet = peerMap.keySet();
            int k = 1 + r.nextInt(keySet.size());
            int cnt = 0;
            for (Id key : keySet) {
                cnt++;
                if (cnt >= k) {
                    return peerMap.get(key);
                }
            }
        }
        return null;
    }

    public Peer getPeerById(Id id) {
        return peerMap.get(id);
    }

    /**
     * Cria um conjunto de peers para ser usado na simula��o. Os peers s�o
     * criados de acordo com os tipos definidos pelo par�metro "types". A
     * cria��o dos peers � feita em paralelo usando f�bricas de peers
     *
     * @param types : mapa contendo a quantidade de peers para cada tipo de
     * comportamento
     * @return total de peers criados | -1 caso nenhum peer seja criado
     */
    public final int createPeers(Map<BehaviorTypes, Integer> types,
            ProtocolType protocol, Environment env, IdFactory factory) {
        ExecutorService exec = Executors.newCachedThreadPool();
        ExecutorCompletionService<Peer> ecs = new ExecutorCompletionService<Peer>(
                exec);
        int total = 0;
        for (BehaviorTypes type : types.keySet()) {

            int cnt = types.get(type);
            while (cnt > 0) {
                ecs.submit(new FactoryTask<Peer>(new PeerFactory(type.getBehavior(), protocol.getProtocol(env), factory)));
                cnt--;
                total++;
            }

        }

        for (int i = 0; i < total; i++) {
            Peer p = null;
            try {
                p = ecs.take().get();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
            if (p != null) {
                addPeer(p);
            }
        }

        return total > 0 ? total : -1;
    }

    public void addPeer(Peer p) {
        synchronized (peerMap) {
            peerMap.put(p.getId(), p);
        }
    }
}
